#include <rtthread.h>

#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>

#include "procfs.h"

struct meminfo_file
{
    uint32_t size;
    uint32_t lines;
    char line[48];
};

static void mem_sprintf(struct meminfo_file *mif, uint32_t l, uint32_t size)
{
    switch (l)
    {
    case 0:
        mif->size = rt_snprintf(mif->line, sizeof(mif->line),
                                "MemTotal:        %12lu B\n", size);
        break;
    case 1:
        mif->size = rt_snprintf(mif->line, sizeof(mif->line),
                                "MemMaxAlloc:     %12lu B\n", size);
        break;
    case 2:
        mif->size = rt_snprintf(mif->line, sizeof(mif->line),
                                "MemAvailable:    %12lu B\n", size);
        break;
    }
}

#ifdef RT_USING_MEMHEAP
static int _list_memheap(struct meminfo_file *mif, rt_list_t *head, int l)
{
    struct rt_memheap *mh, *n;
    struct rt_list_node *node;
    uint32_t size = 0;

    rt_list_for_each_entry_safe(mh, n, struct rt_memheap, head, list)
    {
        switch (l)
        {
        case 0:
            size += mh->pool_size;
            break;
        case 1:
            size += mh->max_used_size;
            break;
        case 2:
            size += mh->available_size;
            break;
        }
    }

    mem_sprintf(mif, l, size);

    return 0;
}

static int list_mem(struct meminfo_file *f, int l)
{
    struct rt_object_information *info;

    info = rt_object_get_information(RT_Object_Class_MemHeap);

    return _list_memheap(f, &info->object_list, l);
}
#else
static int list_mem(struct meminfo_file *f, int l)
{
    uint32_t size = 0;

    switch (l)
    {
    case 0:
    {
        rt_memory_info(&size, RT_NULL, RT_NULL);
    }break;
    case 1:
    {
        rt_memory_info(RT_NULL, RT_NULL, &size);
    }break;
    case 2:
    {
        uint32_t t;

        rt_memory_info(&t, &size, RT_NULL);
        size = t - size;
    }break;
    }

    mem_sprintf(f, l, size);

    return 0;
}
#endif

static int dfs_procfs_open(struct dfs_fd *file)
{
    struct meminfo_file *mif;

    mif = (struct meminfo_file *)rt_calloc(1, sizeof(struct meminfo_file));
    if (!mif)
        return -ENOMEM;

    mif->lines = 3;
    file->data = mif;

    return 0;
}

static int dfs_procfs_close(struct dfs_fd *file)
{
    rt_free(file->data);

    return 0;
}

static int dfs_procfs_read(struct dfs_fd *file, void *buf, size_t count)
{
    int ret;
    struct meminfo_file *mif;
    size_t cnt, pos;

    mif = (struct meminfo_file *)file->data;
    if (file->size >= mif->lines)
        return 0;

    if (file->pos == 0)
    {
        list_mem(mif, file->size);
    }
    if (file->pos >= mif->size)
        return 0;
    pos = file->pos;
    cnt = (count > mif->size) ? mif->size : count;
    rt_memcpy(buf, &mif->line[pos], cnt);
    file->pos += cnt;

    if (file->pos >= mif->size)
    {
        file->size ++;
        file->pos = 0;
    }

    ret = cnt;

    return ret;
}

const struct dfs_file_ops _meminfo_fops =
{
    dfs_procfs_open,
    dfs_procfs_close,
    RT_NULL,
    dfs_procfs_read,
};
